#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>

#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/of_irq.h>

#define BUF_LEN 100

#include "key.h"

//主设备号
int major = 11;
//次设备号
int minor = 0;
//申请数量
int mychar_num = 1;

//自定义字符设备
struct keyirq {

	//定义字符设备
	struct cdev mydev;

	//GPIO
	int gpio;
	//中断号
	int irqno;

	//按键数据
	struct keydata kdat;
	//数据到达标识
	int openflag;

	//自旋锁
	spinlock_t lock;

	//等待读队列
	wait_queue_head_t rq;

	//软中断:tasklet
	struct tasklet_struct tsk;
};

struct keyirq * pgdev = NULL;

int mychar_open(struct inode *pnode, struct file *pfile){

	//将自定义字符设备的地址赋值给 private_data 变量
	//这样后续的操作函数都不需要使用全局变量
	pfile->private_data = (void *)container_of(pnode->i_cdev, struct keyirq, mydev);
	printk("mychar_open is called \n");
	return 0;
}

int mychar_close(struct inode *pnode, struct file *pfile){
	return 0;
}

//读函数
ssize_t mychar_read(struct file *pfile, char __user *pbuf, size_t count, loff_t *pos){
	
	int ret = 0;

	struct keyirq * pgdev = (struct keyirq *)pfile->private_data;

	//判断传入的count是否正确
	if(count < sizeof(struct keydata)){
		printk("count is small \n");
		return -1;
	}
	if(count > sizeof(struct keydata)){
		count = sizeof(struct keydata);
	}

	//若数据没来，此时如何处理
	spin_lock(&pgdev->lock);
	if(pgdev->openflag == 0){
		if(pfile->f_flags & O_NONBLOCK){
			//非阻塞
			spin_unlock(&pgdev->lock);
			printk("No data \n");
			return -1;
		}else{
			//阻塞
			spin_unlock(&pgdev->lock);
			ret = wait_event_interruptible(pgdev->rq, pgdev->openflag == 1);
			if(ret){
				printk("signal wakeup \n");
				return -ERESTARTSYS;
			}
			spin_lock(&pgdev->lock);
		}
	}

	//数据来了
	ret = copy_to_user(pbuf, &pgdev->kdat, count);
	if(ret){
		spin_unlock(&pgdev->lock);
		printk("copy failed \n");
		return -1;
	}

	pgdev->openflag = 0;

	spin_unlock(&pgdev->lock);

	return count;

}

//中断处理函数
irqreturn_t key_irq_handler(int irqno, void * arg){

	
	struct keyirq * pgdev = (struct keyirq *)arg;
	//调度tasklet
	tasklet_schedule(&pgdev->tsk);

	return IRQ_HANDLED;
}

void bottom_irq_handler(unsigned long arg){
	int status1 = 0;
	int status2 = 0;
	struct keyirq * pgdev = (struct keyirq *)arg;

	//获取GPIO上的数据
	status1 = gpio_get_value(pgdev->gpio);
	//防止按键抖动  忙等待延时1ms
	mdelay(1);
	status2 = gpio_get_value(pgdev->gpio);
	//若不等 证明是按键抖动导致
	if(status1 != status2){
		return;
	}


	spin_lock(&pgdev->lock);
	//判断跟现有数据是否相等，若不等则表明有数据到了,若相等直接返回
	if(status1 == pgdev->kdat.status){
		spin_unlock(&pgdev->lock);
		return;
	}

	pgdev->kdat.code = KEY2;
	pgdev->kdat.status = status1;
	pgdev->openflag = 1;
	spin_unlock(&pgdev->lock);

	wake_up(&pgdev->rq);
	return;
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mychar_open,
	.release = mychar_close,
	.read = mychar_read,
};

int __init mychar_init(void)
{
	int ret = 0;
	struct device_node *pnod = NULL;
	//设备号
	dev_t devno = MKDEV(major, minor);
	

	pnod = of_find_node_by_path("/mykey3_node");
		
	if(NULL == pnod){
		printk("find node failed\n");
		return -1;
	}

	pgdev = (struct keyirq *)kmalloc(sizeof(struct keyirq), GFP_KERNEL);
	if(NULL == pgdev){
		printk("kmalloc struct keyirq failed\n");
		return -1;
	}

	pgdev->gpio = of_get_named_gpio(pnod, "key3-gpio", 0);
	pgdev->irqno = irq_of_parse_and_map(pnod, 0);
	
	//从当前设备号开始验证，mychar_num数量的设备号。若系统没有占用，则申请占用mychar_num数量的设备号
	//"mychar": 该设备号在/proc/devices文件中的名称。意义：确保设备号申请成功，及方便后续据此设备号、名称创建设备文件
	ret = register_chrdev_region(devno, mychar_num, "mychar");

	if(ret){
		//系统动态分配
		ret = alloc_chrdev_region(&devno, minor, mychar_num, "mychar");
		if(ret){
			kfree(pgdev);
			pgdev = NULL;
			printk("get devno failed\n");
			return -1;
		}
		//此时设备号是由系统分配的
		major = MAJOR(devno);
	}

	//初始化字符设备,指定所属内核模块，指定操作函数集
	pgdev->mydev.owner = THIS_MODULE;
	cdev_init(&pgdev->mydev, &myops);

	//将cdev对象添加到内核的字符设备链表中
	cdev_add(&pgdev->mydev, devno, mychar_num);

	init_waitqueue_head(&pgdev->rq);

	//初始化自旋锁
	spin_lock_init(&pgdev->lock);


	//初始化tasklet
	tasklet_init(&pgdev->tsk, bottom_irq_handler, (unsigned long)pgdev);

	//申请中断号
	ret = request_irq(pgdev->irqno, key_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "key2Irq", pgdev);
	if(ret){
		printk("request irq failed\n");
		//从内核的字符设备链表中移除该设备
		cdev_del(&pgdev->mydev);
	
		//释放设备号
		unregister_chrdev_region(devno, mychar_num);
		
		kfree(pgdev);
		pgdev = NULL;
		return -1;
		
	}

	return 0;
}

void __exit mychar_exit(void)
{

	dev_t devno = MKDEV(major, minor);

	//释放中断号
	free_irq(pgdev->irqno, pgdev);

	//从内核的字符设备链表中移除该设备
	cdev_del(&pgdev->mydev);

	//释放设备号
	unregister_chrdev_region(devno, mychar_num);

	kfree(pgdev);
	pgdev = NULL;
}

MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);
